package team.nmsg.ge.system.init.shiro;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.hibernate.Criteria;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


import team.nmsg.ge.system.bean.SysOper;
import team.nmsg.ge.system.bean.SysRole;
import team.nmsg.ge.system.bean.SysUser;
import team.nmsg.ge.system.util.MD5Encrypt;
import team.nmsg.ge.system.util.ServerConstants;
import team.nmsg.ge.system.util.ServerParams;
import team.nmsg.ge.system.util.SysDBHelper;

/**
 * Shiro 登陆与授权
 * @author Guodeqi
 *
 */
public class SysAuthorizingRealm extends AuthorizingRealm implements ServerConstants{
	
	private static final Logger logger = LoggerFactory.getLogger(SysAuthorizingRealm.class);
	
	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();  
		
		//授权
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();
		Object roles = session.getAttribute( SESSION_KEY_ROLE_SET_STR );
		Object permissions = session.getAttribute( SESSION_KEY_PERM_SET_STR );
		if(  roles != null ){
			info.addRoles(  (Set<String>)roles  );
		}
		if(  permissions != null ){
			info.addStringPermissions( (Set<String>)permissions);
		}
		
		return  info;
	}

	
	/**
	 * 登陆
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken arg0)
			throws AuthenticationException {
		
		SimpleAuthenticationInfo info = null;
		org.hibernate.Session openHSession = SysDBHelper.getHelper().openHSession();
		Transaction beginTransaction = null;
		try {
			Object credentials = arg0.getCredentials();
			Object principal = arg0.getPrincipal();
			
			if( arg0 instanceof UsernamePasswordToken ){
				UsernamePasswordToken uptoken = (UsernamePasswordToken)arg0;
				char[] password = uptoken.getPassword();
				credentials = new String( password );
			}
			
			String uname = principal.toString();
			String pass = credentials.toString();
			String md5Str = MD5Encrypt.getMD5Str( pass );
			SysUser user = null;
			try {
				Criteria ccc = openHSession.createCriteria(SysUser.class);
				ccc.add(Restrictions.eq("username", uname));
				ccc.add(Restrictions.eq("password", md5Str));
				Object uniqueResult = ccc.uniqueResult();
				if( uniqueResult != null && uniqueResult instanceof SysUser){
					user = (SysUser) uniqueResult;
				}
			} catch (Exception e) {
				logger.error("" , e);
			}
			
			if(  user!= null ){
				info = new SimpleAuthenticationInfo( principal , credentials , getName());
				Long nowTimestamp=System.currentTimeMillis();
//				Long lastUpdateTimestamp=user.getLastUpdateTimestamp();	
//				Long timeLimit=(long) (6L*30L*24L*60L*60L*1000L);
//				if(nowTimestamp-lastUpdateTimestamp>=timeLimit){
//					throw new IncorrectCredentialsException("User password has expired!");
//				}
//				else{
					try {
						user.setLastLoginTimestamp(nowTimestamp);//最后一次登录时间
						long n;
						if(user.getLoginCounter()==null||"".equals(user.getLoginCounter()))
							n=0;
						else
							n=user.getLoginCounter();
						long logNum=n+1;
						user.setLoginCounter( logNum );
						openHSession.saveOrUpdate(user);
						openHSession.flush();
					} catch (Exception e) {
						logger.error("Update "+uname+" login counter failed" , e);
					}
//				}
			}else{
				throw new IncorrectCredentialsException("User name or password is invalid!");
			}
			
			if( user.getUserStatus().intValue() == SysUser.USER_STATUS_ABNORMAL ){
				throw new IncorrectCredentialsException("Abnormal state of the user!");
			}
			
			//将用户权限信息存入session
			Set<String> roles = new HashSet<String>();
			Set<String> permissions = new HashSet<String>();
			
			Set<SysRole> sysRoles = user.getRoles();
			
			Set<SysRole> sysRolesCopy = new HashSet<SysRole>();
			
			for( SysRole role :  sysRoles){
				roles.add(role.getRoleName());
				Set<SysOper> opers = role.getOpers();
				for(   SysOper oper : opers ){
					permissions.add(  oper.getOperName());
				}
				
				SysRole copyOne = new SysRole();
				copyOne.setRoleId(role.getRoleId());
				copyOne.setRoleName(role.getRoleName());
				copyOne.setRoleRemark(role.getRoleRemark());
				sysRolesCopy.add(copyOne );
			}
			
			Subject subject = SecurityUtils.getSubject();
			Session session = subject.getSession();
			session.setAttribute(SESSION_KEY_ROLE_SET_STR, roles);
			session.setAttribute(SESSION_KEY_PERM_SET_STR, permissions);
			session.setAttribute(SESSION_KEY_USER, user );
			
			session.setTimeout( ServerParams.SESSION_TIMEOUT );
			session.setAttribute(SESSION_KEY_USERNAME_STR, user.getUsername());
			session.setAttribute(SESSION_KEY_SESSIONID_STR, session.getId() );
			
			boolean isAdmin = false;
			if( roles.contains( ServerParams.ADMIN_NAME ) ){
				isAdmin = true;
			}
			session.setAttribute(SESSION_KEY_ISADMIN_BOOL, isAdmin );
			session.setAttribute(SESSION_KEY_SET_NMSROLE, sysRolesCopy );
			
			if (!ServerParams.oneUserMultiPlace) {
				// 踢出已在线用户
				Collection<Session> activeSessions = sessionDao
						.getActiveSessions();
				Iterator<Session> iterator = activeSessions.iterator();
				for (; iterator.hasNext();) {
					Session next = iterator.next();
					Object attribute = next
							.getAttribute(SESSION_KEY_USERNAME_STR);
					if (!next.getId().equals(session.getId())
							&& attribute != null
							&& attribute.toString().equals(user.getUsername())) {
						next.setAttribute(SESSION_KEY_KICKOUT, "true");
					}
				}
			}
			
			logger.debug("ROLES: "+roles.size()  +"||" + roles);
			logger.debug("PERMISSIONS :"+permissions.size() +"||"+ permissions);
			logger.debug(Thread.currentThread().getName());
		} catch (AuthenticationException e) {
			throw e;
		}finally{
			openHSession.close();
		}
		
		return info;
	}

	public static boolean isAdmin(){
		return (Boolean) getShiroSessionAttr(SESSION_KEY_ISADMIN_BOOL);
	}
	
	public static Object getShiroSessionAttr(String key ){
		Subject subject = SecurityUtils.getSubject();
		Session session = subject.getSession();
		Object attribute = session.getAttribute(key);
		return attribute;
	}
	
	
	public static Set<SysRole> getCUserRoles(){
		try {
			Object shiroSessionAttr = getShiroSessionAttr(SESSION_KEY_SET_NMSROLE);
			
			if( shiroSessionAttr != null  ) {
				Set<SysRole> nmsRoles = (Set<SysRole>) shiroSessionAttr;
				return nmsRoles ; 
			}
		} catch (Exception e) {
			logger.error("" , e);
		}
		return null;
	}
	
	public static SysUser getCUser(){
		try {
			Object shiroSessionAttr = getShiroSessionAttr(SESSION_KEY_USER);
			
			if( shiroSessionAttr != null  ) {
				SysUser uuu = (SysUser) shiroSessionAttr;
				return uuu ; 
			}
		} catch (Exception e) {
			logger.error("" , e);
		}
		return null;
	}
	
	
	

	private static SessionDAO sessionDao = null;
	
	@Autowired
	public void setSessionDao(SessionDAO sessionDao) {
		SysAuthorizingRealm.sessionDao = sessionDao;
	}
	
	public static SessionDAO getSessionDAO(){
		return sessionDao;
	}


	public static Session getShiroSession(String sid ){
		
		Session readSession = null;
		try {
			readSession = sessionDao.readSession(sid);
		} catch (UnknownSessionException e) {
			logger.error("Session not exist : " + e.getMessage()  ,  e);
		}
		
		return readSession;
	}


}
